// PanelFolderChange.cpp

#include "StdAfx.h"


#include "Directory.h"

#include "Common/String/StringConvert.h"
#include "Common/FileSystem/FileDir.h"
#include "Common/FileSystem/FileItem.h"
#include "Common/FileSystem/FileName.h"
#include "Common/FileSystem/SysIconUtils.h"
#include "Common/FileSystem/Wildcard.h"

#include "Common/MS/PropVariant.h"

#include "Common/7Zip/PropID.h"

#ifdef UNDER_CE
#include "Common/FileSystem/FSFolder.h"
#else
#include "Common/FileSystem/FSDrives.h"
#endif
#include "Common/Lang/LangUtils.h"
#include "Common/FileSystem/RootFolder.h"

#include "../../resource.h"



namespace FileManager
{
namespace FileSystem
{


using namespace Common;

void CDirectory::SetToRootFolder()
{
	_folder.Release();
	_library.Free();
	Common::FileSystem::CRootFolder* rootFolderSpec = new CComObject<Common::FileSystem::CRootFolder>;
	rootFolderSpec->FinalConstruct();
	_folder = rootFolderSpec;
	rootFolderSpec->Init();
	//_folder changed 
	//update path
	LoadFullPath();
}

HRESULT CDirectory::BindToPath(const CString &fullPath, const CString &arcFormat, bool &archiveIsOpened, bool &encrypted)
{
	archiveIsOpened = false;
	encrypted = false;
	CDisableTimerProcessing disableTimerProcessing1(*this);

	if (_parentFolders.size() > 0)
	{
		const CString &virtPath = _parentFolders.back()->VirtualPath;
		if (fullPath.Left(virtPath.GetLength()) == virtPath)
		{
			bool bNewFolder(false);
			for (;;)
			{
				CComPtr<IFolderFolder> newFolder;
				HRESULT res = _folder->BindToParentFolder(&newFolder);
				if (!newFolder || res != S_OK)
				{
					break;
				}
				_folder = newFolder;
				bNewFolder = true;
			}
			CStringVector parts;
			Common::FileSystem::SplitPathToParts(fullPath.Mid(virtPath.GetLength()), parts);
			for (size_t i = 0; i < parts.size(); i++)
			{
				const CString &s = parts[i];
				if ((i == 0 || i == parts.size() - 1) && s.IsEmpty())
				{
					continue;
				}
				CComPtr<IFolderFolder> newFolder;
				HRESULT res = _folder->BindToFolder(s, &newFolder);
				if (!newFolder || res != S_OK)
				{
					break;
				}
				_folder = newFolder;
				bNewFolder = true;
			}
			//_folder changed 
			//update path
			if (bNewFolder)
				LoadFullPath();
			return S_OK;
		}
	}

	CloseOpenFolders();
	CString sysPath = fullPath;
	Common::FileSystem::File::CFileInfo fileInfo;
	CStringVector reducedParts;
	while (!sysPath.IsEmpty())
	{
		if (fileInfo.Find(sysPath))
		{
			break;
		}
		int pos = sysPath.ReverseFind(CHAR_PATH_SEPARATOR);
		if (pos < 0)
		{
			sysPath.Empty();
		}
		else
		{
			if (reducedParts.size() > 0 || pos < sysPath.GetLength() - 1)
			{
				reducedParts.push_back(sysPath.Mid(pos + 1));
			}
			sysPath = sysPath.Left(pos);
		}
	}
	SetToRootFolder();
	CComPtr<IFolderFolder> newFolder;
	if (sysPath.IsEmpty())
	{
		if (_folder->BindToFolder(fullPath, &newFolder) == S_OK)
		{
			_folder = newFolder;
			//_folder changed 
			//update path
			LoadFullPath();
		}
	}
	else if (fileInfo.IsDir())
	{
		Common::FileSystem::FileName::NormalizeDirPathPrefix(sysPath);
		if (_folder->BindToFolder(sysPath, &newFolder) == S_OK)
		{
			_folder = newFolder;
			//_folder changed 
			//update path
			LoadFullPath();
		}
	}
	else
	{
		CString dirPrefix;
		if (!Common::FileSystem::GetOnlyDirPrefix(sysPath, dirPrefix))
		{
			dirPrefix.Empty();
		}
		if (_folder->BindToFolder(dirPrefix, &newFolder) == S_OK)
		{
			_folder = newFolder;
			//_folder changed 
			//update path
			LoadFullPath();
			CString fileName;
			if (Common::FileSystem::GetOnlyName(sysPath, fileName))
			{
				HRESULT res = OpenItemAsArchive(fileName, arcFormat, encrypted);
				if (res != S_FALSE)
				{
					if(S_OK != res)
					{
						return res;
					}
				}

				if (res == S_OK)
				{
					archiveIsOpened = true;
					for (auto it = reducedParts.rbegin(); it != reducedParts.rend(); ++it)
					{
						CComPtr<IFolderFolder> newFolder;
						_folder->BindToFolder(*it, &newFolder);
						if (!newFolder)
						{
							break;
						}
						_folder = newFolder;
						//_folder changed 
						//update path
						LoadFullPath();
					}
				}
			}
		}
	}
	return S_OK;
}

HRESULT CDirectory::BindToPathAndRefresh(const CString &path)
{
	CDisableTimerProcessing disableTimerProcessing1(*this);
	bool archiveIsOpened, encrypted;
	HRESULT hRes(BindToPath(path, CString(), archiveIsOpened, encrypted));
	if(S_OK != hRes)
	{
		return hRes;
	}
	return S_OK;
}

CString GetFolderPath(IFolderFolder* folder)
{
	CComPropVariant prop;
	if (folder->GetFolderProperty(kpidPath, &prop) == S_OK)
		if (prop.vt == VT_BSTR)
		{
			return (wchar_t*)prop.bstrVal;
		}
	return CString();
}

void CDirectory::LoadFullPath()
{
	_currentFolderPrefix.Empty();
	for (size_t i = 0; i < _parentFolders.size(); i++)
	{
		const auto &folderLink = _parentFolders[i];
		_currentFolderPrefix += GetFolderPath(folderLink->ParentFolder);
		_currentFolderPrefix += folderLink->ItemName;
		_currentFolderPrefix += CHAR_PATH_SEPARATOR;
	}
	if (_folder)
	{
		_currentFolderPrefix += GetFolderPath(_folder);
	}
}

int GetRealIconIndex(LPCWSTR path, DWORD attributes)
{
	int index = -1;
	if (Common::FileSystem::GetRealIconIndex(path, attributes, index) != 0)
	{
		return index;
	}
	return -1;
}


void CDirectory::OpenParentFolder(CString &selectedItem)
{
	//LoadFullPath(); // Maybe we don't need it ??
	selectedItem.Empty();
	if (!GetCurrentDirectory().IsEmpty() &&
		GetCurrentDirectory().Left(GetCurrentDirectory().GetLength() - 1) == CHAR_PATH_SEPARATOR)
	{
		selectedItem = GetCurrentDirectory();
		selectedItem.Delete(selectedItem.GetLength() - 1);
		if (selectedItem != _T("\\\\."))
		{
			int pos = selectedItem.ReverseFind(CHAR_PATH_SEPARATOR);
			if (pos >= 0)
			{
				selectedItem = selectedItem.Mid(pos + 1);
			}
		}
	}

	CDisableTimerProcessing disableTimerProcessing1(*this);
	CComPtr<IFolderFolder> newFolder;
	_folder->BindToParentFolder(&newFolder);
	if (newFolder)
	{
		_folder = newFolder;
		//_folder changed 
		//update path
		LoadFullPath();
	}
	else
	{
		if (_parentFolders.empty())
		{
			SetToRootFolder();
			if (selectedItem.IsEmpty())
			{
				selectedItem = GetItemName(0);
			}
		}
		else
		{
			_folder.Release();
			_library.Free();
			auto link = _parentFolders.back();
			_folder = link->ParentFolder;
			_library.Attach(link->Library.Detach());
			selectedItem = link->ItemName;
			if (_parentFolders.size() > 1)
			{
				OpenParentArchiveFolder();
			}
			_parentFolders.pop_back();
			if (_parentFolders.empty())
			{
				_flatMode = _flatModeForDisk;
			}
			//_folder changed 
			//update path
			LoadFullPath();
		}
	}
}

void CDirectory::CloseOpenFolders()
{
	while (_parentFolders.size() > 0)
	{
		_folder.Release();
		_library.Free();
		_folder = _parentFolders.back()->ParentFolder;
		_library.Attach(_parentFolders.back()->Library.Detach());
		if (_parentFolders.size() > 1)
		{
			OpenParentArchiveFolder();
		}
		_parentFolders.pop_back();
	}
	_flatMode = _flatModeForDisk;
	_folder.Release();
	_library.Free();
	//_folder changed 
	//update path
	LoadFullPath();
}
/*
void CDirectory::OpenRootFolder()
{
	CDisableTimerProcessing disableTimerProcessing1(*this);
	_parentFolders.clear();
	SetToRootFolder();
	RefreshListCtrl(CString(), -1, true, CStringVector());

}*/

void CDirectory::OpenDrivesFolder()
{
	CloseOpenFolders();
#ifdef UNDER_CE
	NFsFolder::CFSFolder* folderSpec = new NFsFolder::CFSFolder;
	_folder = folderSpec;
	folderSpec->InitToRoot();
#else
	Common::FileSystem::CFSDrives* folderSpec = new CComObject<Common::FileSystem::CFSDrives>;
	folderSpec->FinalConstruct();
	_folder = folderSpec;
	folderSpec->Init();
	//_folder changed 
	//update path
	LoadFullPath();
#endif
}

void CDirectory::OpenFolder(int index)
{
	if (index == kParentIndex)
	{
		CString selected;
		OpenParentFolder(selected);
		return;
	}
	CComPtr<IFolderFolder> newFolder;
	_folder->BindToFolder(index, &newFolder);
	if (!newFolder)
	{
		return;
	}
	_folder = newFolder;
	//_folder changed 
	//update path
	LoadFullPath();
}

} //namespace FileSystem
} //namespace FileManager